package ca.etsmtl.log730.projet2.connectors.server.tcpip;

import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.socket.nio.NioSession;

import ca.etsmtl.log730.projet2.connectors.AbstractSetup;
import ca.etsmtl.log730.projet2.connectors.msg.document.AccessCMD;
import ca.etsmtl.log730.projet2.connectors.msg.document.DocumentCMD;
import ca.etsmtl.log730.projet2.connectors.msg.document.DocumentCMD.ModType;
import ca.etsmtl.log730.projet2.connectors.msg.document.DocumentEVT;
import ca.etsmtl.log730.projet2.connectors.msg.user.UserCMD;
import ca.etsmtl.log730.projet2.connectors.msg.user.UserCMD.Action;
import ca.etsmtl.log730.projet2.connectors.msg.user.UserEVT;
import ca.etsmtl.log730.projet2.connectors.server.ServerAdapter;
import ca.etsmtl.log730.projet2.documents.Document;
import ca.etsmtl.log730.projet2.documents.DocumentController;
import ca.etsmtl.log730.projet2.documents.DocumentListener;
import ca.etsmtl.log730.projet2.users.User;
import ca.etsmtl.log730.projet2.users.UserController;
import ca.etsmtl.log730.projet2.users.UserListener;

public class DistantServerAdapter implements ServerAdapter, IoHandler, DocumentListener, UserListener {
	private DocumentController documentController = new DocumentController();
	private UserController userController = new UserController();
	
	public DistantServerAdapter() {
		userController.addUserListener(documentController);
		documentController.addDocumentListener(this);
		userController.addUserListener(this);
	}
	private Set<IoSession> sessions = new HashSet<IoSession>();
	
	@Override
	public void exceptionCaught(IoSession ios, Throwable o)
			throws Exception {
	}

	@Override
	public void messageReceived(IoSession ios, Object o) throws Exception {
		System.out.println(o.toString());
		if(o instanceof UserCMD) {
			UserCMD uc = (UserCMD) o;
			if(uc.getAction() == Action.JOINING) {
				userController.addUser(uc.getUser());
				//ADD
			} else if (uc.getAction() == Action.LEAVING) {
				//REMOVE
				userController.removeUser(uc.getUser());
			}
			
		} else if(o instanceof AccessCMD) {
			AccessCMD a = (AccessCMD) o;
			if(a.getAction() == AccessCMD.Action.ADD) {
				//TRY ADD ACCESS CMD
				if(a.getMode() == AccessCMD.Mode.EDIT) {
					documentController.addEditor(a.getDocId(), a.getUser());
				} else {
					documentController.addViewer(a.getDocId(), a.getUser());
				}
			} else if(a.getAction() == AccessCMD.Action.REMOVE) {
				//TRY REMOVE ACCESS CMD
				if(a.getMode() == AccessCMD.Mode.EDIT) {
					documentController.removeViewer(a.getDocId(), a.getUser());
				} else {
					documentController.removeViewer(a.getDocId(), a.getUser());
				}
			}
		} else if(o instanceof DocumentCMD) {
			DocumentCMD d = (DocumentCMD) o;
			if(d.getModType() == ModType.ADD) {
				//ADD DOC
				
			} else if(d.getModType() == ModType.CHANGE) {
				//UPDATE DOC
				documentController.setNewValues(d.getDocId(), d.getDocument(), d.getUser());
			} else if(d.getModType() == ModType.REMOVE) {
				//REMOVE DOC
			}
		}
	}

	@Override
	public void messageSent(IoSession ios, Object o) throws Exception {
	}

	@Override
	public void sessionClosed(IoSession ios) throws Exception {
		sessions.remove(ios);
	}

	@Override
	public void sessionCreated(IoSession ios) throws Exception {
		sessions.add(ios);
		NioSession ns = (NioSession) ios;
		System.out.println("NEW SESSION! :" + ns.getRemoteAddress().toString());
	}

	@Override
	public void sessionIdle(IoSession ios, IdleStatus o) throws Exception {
	}

	@Override
	public void sessionOpened(IoSession ios) throws Exception {
	}

	@Override
	public void userJoined(final User u) {
		new Thread(new Runnable() {

			@Override
			public void run() {
				for(IoSession io: sessions) {
					io.write(new UserEVT(UserEVT.Action.JOINED, u));
				}
			}
			
		}).start();
	}

	@Override
	public void userQuitted(final User u) {
		new Thread(new Runnable() {

			@Override
			public void run() {
				for(IoSession io: sessions) {
					io.write(new UserEVT(UserEVT.Action.QUITTED, u));
				}
			}
		}).start();
	}

	@Override
	public void documentChanged(final UUID id,final Document doc) {
		new Thread(new Runnable() {

			@Override
			public void run() {
				for(IoSession io: sessions) {
					io.write(new DocumentEVT(DocumentEVT.ModType.CHANGED, id, doc));
				}
			}
		}).start();
	}

	@Override
	public void documentAdded(final UUID id,final Document doc) {
		new Thread(new Runnable() {

			@Override
			public void run() {
				for(IoSession io: sessions) {
					io.write(new DocumentEVT(DocumentEVT.ModType.ADDED, id, doc));
				}
			}
		}).start();
			
	}

	@Override
	public void documentRemoved(final UUID id,final Document doc) {
		new Thread(new Runnable() {

			@Override
			public void run() {
				for(IoSession io: sessions) {
					io.write(new DocumentEVT(DocumentEVT.ModType.REMOVED, id, doc));
				}
			}
		}).start();
	}

	private AbstractSetup serverSetup;
	@Override
	public void open(AbstractSetup ss) {
		serverSetup = ss;
//		serverSetup .setServerAdapter(this);
		serverSetup.open();
	}

	@Override
	public void close() {
		serverSetup.close();
	}


	private User user;
	@Override
	public void setUser(User u) {
		user = u;
	}

	@Override
	public User getUser() {
		return user;
	}

}

